home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / XMLTOOLS.PY < prev    next >
Encoding:
Python Source  |  2000-05-04  |  13.5 KB  |  432 lines

  1. ##############################################################################
  2. # Zope Public License (ZPL) Version 1.0
  3. # -------------------------------------
  4. # Copyright (c) Digital Creations.  All rights reserved.
  5. # This license has been certified as Open Source(tm).
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. # 1. Redistributions in source code must retain the above copyright
  10. #    notice, this list of conditions, and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions, and the following disclaimer in
  13. #    the documentation and/or other materials provided with the
  14. #    distribution.
  15. # 3. Digital Creations requests that attribution be given to Zope
  16. #    in any manner possible. Zope includes a "Powered by Zope"
  17. #    button that is installed by default. While it is not a license
  18. #    violation to remove this button, it is requested that the
  19. #    attribution remain. A significant investment has been put
  20. #    into Zope, and this effort will continue if the Zope community
  21. #    continues to grow. This is one way to assure that growth.
  22. # 4. All advertising materials and documentation mentioning
  23. #    features derived from or use of this software must display
  24. #    the following acknowledgement:
  25. #      "This product includes software developed by Digital Creations
  26. #      for use in the Z Object Publishing Environment
  27. #      (http://www.zope.org/)."
  28. #    In the event that the product being advertised includes an
  29. #    intact Zope distribution (with copyright and license included)
  30. #    then this clause is waived.
  31. # 5. Names associated with Zope or Digital Creations must not be used to
  32. #    endorse or promote products derived from this software without
  33. #    prior written permission from Digital Creations.
  34. # 6. Modified redistributions of any form whatsoever must retain
  35. #    the following acknowledgment:
  36. #      "This product includes software developed by Digital Creations
  37. #      for use in the Z Object Publishing Environment
  38. #      (http://www.zope.org/)."
  39. #    Intact (re-)distributions of any official Zope release do not
  40. #    require an external acknowledgement.
  41. # 7. Modifications are encouraged but must be packaged separately as
  42. #    patches to official Zope releases.  Distributions that do not
  43. #    clearly separate the patches from the original work must be clearly
  44. #    labeled as unofficial distributions.  Modifications which do not
  45. #    carry the name Zope may be packaged in any form, as long as they
  46. #    conform to all of the clauses above.
  47. # Disclaimer
  48. #   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
  49. #   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  51. #   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
  52. #   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  53. #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  54. #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  55. #   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. #   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  57. #   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  58. #   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  59. #   SUCH DAMAGE.
  60. # This software consists of contributions made by Digital Creations and
  61. # many individuals on behalf of Digital Creations.  Specific
  62. # attributions are listed in the accompanying credits file.
  63. ##############################################################################
  64.  
  65. """WebDAV XML parsing tools. Note that this module does just
  66.    enough for the purposes of DAV - it is not intended as a
  67.    general xml toolkit, and will probably eventually go away
  68.    in favor of a standard xml package once some issues are
  69.    worked out."""
  70.  
  71. __version__='$Revision: 1.9 $'[11:-2]
  72.  
  73. import sys, os, string
  74. import Shared.DC.xml.xmllib
  75. from Acquisition import Implicit
  76. from cStringIO import StringIO
  77.  
  78.  
  79. type_document=0
  80. type_element=1
  81. type_attribute=2
  82. type_text=3
  83. type_cdata=4
  84. type_entityref=5
  85. type_entity=6
  86. type_procinst=7
  87. type_comment=8
  88. type_notation=9
  89.  
  90.  
  91. class Node(Implicit):
  92.     """Common base class for Node objects."""
  93.     __name__=''
  94.     __value__=''
  95.     __attrs__=[]
  96.     __nodes__=[]
  97.     __nskey__=''
  98.  
  99.     def name(self):  return self.__name__
  100.     def attrs(self): return self.__attrs__
  101.     def value(self): return self.__value__
  102.     def nodes(self): return self.__nodes__
  103.     def nskey(self): return self.__nskey__
  104.     
  105.     def addNode(self, node):
  106.         self.__nodes__.append(node.__of__(self))
  107.  
  108.     def namespace(self):
  109.         nskey=self.__nskey__
  110.         while 1:
  111.             if hasattr(self, '__nsdef__'):
  112.                 val=self.__nsdef__.get(nskey, None)
  113.                 if val is not None: return val
  114.             if not hasattr(self, 'aq_parent'):
  115.                 return ''
  116.             self=self.aq_parent
  117.  
  118.     def elements(self, name=None, ns=None, lower=string.lower):
  119.         nodes=[]
  120.         name=name and lower(name)
  121.         for node in self.__nodes__:
  122.             if node.__type__==type_element and \
  123.                ((name is None) or (lower(node.__name__)==name)) and \
  124.                ((ns is None) or (node.namespace()==ns)):
  125.                 nodes.append(node)
  126.         return nodes
  127.     
  128.     def __getitem__(self, n):
  129.         return self.__nodes__[n]
  130.  
  131.     def qname(self):
  132.         ns=self.__nskey__
  133.         if ns: ns='%s:' % ns
  134.         return '%s%s' % (ns, self.__name__)
  135.  
  136.     def toxml(self):
  137.         return self.__value__
  138.  
  139.     def strval(self):
  140.         return self.toxml()
  141.  
  142.  
  143. class Document(Node):
  144.     def __init__(self, encoding='utf-8', stdalone=''):
  145.         self.__name__ ='document'
  146.         self.__nodes__=[]
  147.         self.encoding=encoding
  148.         self.stdalone=stdalone
  149.         self.document=self
  150.         
  151.     def toxml(self):
  152.         result=['<?xml version="1.0" encoding="%s"?>' % self.encoding]
  153.         for node in self.__nodes__:
  154.             result.append(node.toxml())
  155.         return string.join(result, '')
  156.  
  157.     #def __del__(self):
  158.     #    self.document=None
  159.     #    print 'bye!'
  160.         
  161. class Element(Node):
  162.     __type__=type_element
  163.     
  164.     def __init__(self, name, attrs={}):
  165.         self.__name__ =name
  166.         self.__attrs__=[]
  167.         self.__nodes__=[]
  168.         self.__nsdef__={}
  169.         self.__nskey__=''
  170.         for name, val in attrs.items():
  171.             attr=Attribute(name, val)
  172.             self.__attrs__.append(attr)
  173.         self.ns_parse()
  174.         parts=string.split(self.__name__, ':')
  175.         if len(parts) > 1:
  176.             self.__nskey__=parts[0]
  177.             self.__name__=string.join(parts[1:], ':')
  178.             
  179.     def ns_parse(self):
  180.         nsdef=self.__nsdef__={}
  181.         for attr in self.attrs():
  182.             name, val=attr.name(), attr.value()
  183.             key=string.lower(name)
  184.             if key[:6]=='xmlns:':
  185.                 nsdef[name[6:]]=val
  186.             elif key=='xmlns':
  187.                 nsdef['']=val
  188.  
  189.     def fixup(self):
  190.         self.__attrs__=map(lambda n, s=self: n.__of__(s), self.__attrs__)
  191.         
  192.     def get_attr(self, name, ns=None, default=None):
  193.         for attr in self.__attrs__:
  194.             if attr.name()==name and (ns is None) or (ns==attr.namespace()):
  195.                 return attr
  196.         return default
  197.  
  198.     def del_attr(self, name):
  199.         attrs=[]
  200.         for attr in self.__attrs__:
  201.             if attr.name() != name:
  202.                 attrs.append(attr)
  203.         self.__attrs__=attrs
  204.  
  205.     def remap(self, dict, n=0, top=1):
  206.         # The remap method effectively rewrites an element and all of its
  207.         # children, consolidating namespace declarations into the element
  208.         # on which the remap function is called and fixing up namespace
  209.         # lookup structures.
  210.         nsval=self.namespace()
  211.         if not nsval: nsid=''
  212.         elif not dict.has_key(nsval):
  213.             nsid='ns%d' % n
  214.             dict[nsval]=nsid
  215.             n=n+1
  216.         else: nsid=dict[nsval]
  217.         for attr in self.__attrs__:
  218.             dict, n=attr.remap(dict, n, 0)
  219.         for node in self.elements():
  220.             dict, n=node.remap(dict, n, 0)
  221.         attrs=[]
  222.         for attr in self.__attrs__:
  223.             name=attr.__name__
  224.             if not (((len(name) >= 6) and (name[:6]=='xmlns:')) or \
  225.                     name=='xmlns'):
  226.                 attrs.append(attr)
  227.         self.__attrs__=attrs
  228.         self.__nsdef__={}
  229.         self.__nskey__=nsid
  230.         if top:
  231.             attrs=self.__attrs__
  232.             keys=dict.keys()
  233.             keys.sort()
  234.             for key in keys:
  235.                 attr=Attribute('xmlns:%s' % dict[key], key)
  236.                 attrs.append(attr.__of__(self))
  237.             self.__attrs__=attrs
  238.             self.ns_parse()
  239.         return dict, n
  240.  
  241.     def toxml(self):
  242.         qname=self.qname()
  243.         result=['<%s' % qname]
  244.         for attr in self.__attrs__:
  245.             result.append(attr.toxml())
  246.         if not self.__value__ and not self.__nodes__:
  247.             result.append('/>')
  248.         else:
  249.             result.append('>')
  250.             for node in self.__nodes__:
  251.                 result.append(node.toxml())
  252.             result.append('</%s>' % qname)
  253.         return string.join(result, '')
  254.  
  255.     def strval(self, top=1):
  256.         if not self.__value__ and not self.__nodes__:
  257.             return ''
  258.         result=map(lambda n: n.toxml(), self.__nodes__)
  259.         return string.join(result, '')
  260.  
  261. class Attribute(Node):
  262.     __type__=type_attribute
  263.     def __init__(self, name, val):
  264.         self.__name__=name
  265.         self.__value__=val
  266.         self.__nskey__=''
  267.         parts=string.split(name, ':')
  268.         if len(parts) > 1:
  269.             pre=string.lower(parts[0])
  270.             if not (pre in ('xml', 'xmlns')):
  271.                 self.__nskey__=parts[0]
  272.                 self.__name__=string.join(parts[1:], ':')
  273.  
  274.     def remap(self, dict, n=0, top=1):
  275.         nsval=self.namespace()
  276.         if not nsval: nsid=''
  277.         elif not dict.has_key(nsval):
  278.             nsid='ns%d' % n
  279.             dict[nsval]=nsid
  280.             n=n+1
  281.         else: nsid=dict[nsval]
  282.         self.__nskey__=nsid
  283.         return dict, n
  284.  
  285.     def toxml(self):
  286.         ns=self.__nskey__
  287.         if ns: ns='%s:' % ns
  288.         return ' %s%s="%s"' % (ns, self.__name__, self.__value__)
  289.         
  290. class Text(Node):
  291.     __name__='#text'
  292.     __type__=type_text
  293.     def __init__(self, val):
  294.         self.__value__=val
  295.     def toxml(self):
  296.         return escape(self.__value__)
  297.  
  298. class CData(Node):
  299.     __type__=type_cdata
  300.     __name__='#cdata'
  301.     def __init__(self, val):
  302.         self.__value__=val
  303.     def toxml(self):
  304.         return '<![CDATA[%s]]>' % self.__value__
  305.  
  306. class EntityRef(Node):
  307.     __name__='#entityref'
  308.     __type__=type_entityref
  309.     def __init__(self, val):
  310.         self.__value__=val        
  311.     def toxml(self):
  312.         return '&%s;' % self.__value__
  313.  
  314. class Entity(Node):
  315.     __name__='#entity'
  316.     __type__=type_entity
  317.     def __init__(self, name, pubid, sysid, nname):
  318.         self.__value__=val
  319.     def toxml(self):
  320.         return ''
  321.  
  322. class ProcInst(Node):
  323.     __type__=type_procinst
  324.     def __init__(self, name, val):
  325.         self.__name__=name
  326.         self.__value__=val
  327.     def toxml(self):
  328.         return '<?%s %s?>' % (self.__name__, self.__value__)
  329.  
  330. class Comment(Node):
  331.     __name__='#comment'
  332.     __type__=type_comment
  333.     def __init__(self, val):
  334.         self.__value__=val
  335.     def toxml(self):
  336.         return '<!--%s-->' % self.__value__
  337.  
  338.  
  339.  
  340.  
  341.  
  342. class XmlParser(Shared.DC.xml.xmllib.XMLParser):
  343.     def __init__(self):
  344.         Shared.DC.xml.xmllib.XMLParser.__init__(self)
  345.         self.root=None
  346.         self.node=None
  347.         
  348.     def parse(self, data):
  349.         self.feed(data)
  350.         self.close()
  351.         return self.root
  352.     
  353.     def add(self, node):
  354.         self.node.addNode(node)
  355.  
  356.     def push(self, node):
  357.         self.node.addNode(node)
  358.         self.node=self.node.__nodes__[-1]
  359.  
  360.     def pop(self):
  361.         self.node=self.node.aq_parent
  362.  
  363.     def unknown_starttag(self, name, attrs):
  364.         node=Element(name, attrs)
  365.         self.push(node)
  366.         # Fixup aq chain!
  367.         self.node.fixup()
  368.  
  369.     def unknown_endtag(self, name):
  370.         self.pop()
  371.  
  372.     def handle_xml(self, encoding, stdalone):
  373.         self.root=Document(encoding, stdalone)
  374.         self.node=self.root
  375.  
  376.     def handle_doctype(self, tag, pubid, syslit, data):
  377.         pass
  378.  
  379.     def handle_entity(self, name, strval, pubid, syslit, ndata):
  380.         self.add(Entity(name, strval, pubid, syslit, ndata))
  381.  
  382.     def handle_cdata(self, data):
  383.         self.add(CData(data))
  384.  
  385.     def handle_proc(self, name, data):
  386.         self.add(ProcInst(name, data))
  387.  
  388.     def handle_comment(self, data):
  389.         self.add(Comment(data))
  390.  
  391.     def handle_data(self, data):
  392.         self.add(Text(data))
  393.  
  394.     def unknown_entityref(self, data):
  395.         self.add(EntityRef(data))
  396.  
  397.  
  398.  
  399. def escape(data, rmap={}, replace=string.replace):
  400.     data=replace(data, "&", "&")
  401.     data=replace(data, "<", "<")
  402.     data=replace(data, ">", ">")
  403.     for key, val in rmap.items():
  404.         data=replace(data, key, val)
  405.     return data
  406.  
  407. def remap(data, dict={'DAV:': 'd'}):
  408.     root=XmlParser().parse(data)
  409.     root.elements()[0].remap(dict, 0)
  410.     return root.toxml()
  411.